<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="本文主要是uboot——uboot下命令的执行流程的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV05-02-U-Boot-04-uboot下命令的执行">
<meta property="og:url" content="https://sumumm.github.io/post/14f3979e.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="本文主要是uboot——uboot下命令的执行流程的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116075502350.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116133256624.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116165331445.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116165716871.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117093804710.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117095724996.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117095945588.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117100435266.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116190843990.png">
<meta property="article:published_time" content="2024-11-19T15:38:28.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.043Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="LV05-系统镜像">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116075502350.png">


<link rel="canonical" href="https://sumumm.github.io/post/14f3979e.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":"","permalink":"https://sumumm.github.io/post/14f3979e.html","path":"post/14f3979e.html","title":"LV05-02-U-Boot-04-uboot下命令的执行"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV05-02-U-Boot-04-uboot下命令的执行 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E4%B8%BB%E5%BE%AA%E7%8E%AF%E5%9C%A8%E5%93%AA"><span class="nav-text">一、主循环在哪</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%AF%BB%E6%89%BE%E4%B8%BB%E5%BE%AA%E7%8E%AF%E5%87%BD%E6%95%B0"><span class="nav-text">1. 寻找主循环函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-main%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">1.1 main函数在哪？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-1-u-boot-lds"><span class="nav-text">1.1.1 u-boot.lds</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-2-vectors-S"><span class="nav-text">1.1.2 vectors.S</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-3-start-S"><span class="nav-text">1.1.3  start.S</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-4-crt0-S"><span class="nav-text">1.1.4 crt0.S</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-board-init-r"><span class="nav-text">1.2 board_init_r()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-initcall-run-list"><span class="nav-text">1.3 initcall_run_list()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-init-sequence-r"><span class="nav-text">1.4 init_sequence_r</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-run-main-loop"><span class="nav-text">2. run_main_loop()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-main-loop"><span class="nav-text">3. main_loop</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E6%80%BB%E7%BB%93%E4%B8%80%E4%B8%8B"><span class="nav-text">4. 总结一下</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81main-loop%E5%9C%A8%E5%81%9A%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">二、main_loop在做什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-main-loop"><span class="nav-text">1. main_loop()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-cli-loop"><span class="nav-text">2. cli_loop()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-parse-file-outer"><span class="nav-text">3. parse_file_outer()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-parse-stream-outer"><span class="nav-text">4. parse_stream_outer()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-cmd-process"><span class="nav-text">5. cmd_process()</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-find-cmd"><span class="nav-text">5.1 find_cmd()</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#5-1-1-linker-lists"><span class="nav-text">5.1.1 linker_lists</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-1-ll-entry-start"><span class="nav-text">5.1.1.1 ll_entry_start</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-2-ll-entry-end"><span class="nav-text">5.1.1.2 ll_entry_end</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-3-ll-entry-count"><span class="nav-text">5.1.1.3 ll_entry_count</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-4-ll-entry-declare"><span class="nav-text">5.1.1.4 ll_entry_declare</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-5-linker-list%E5%88%86%E6%9E%90"><span class="nav-text">5.1.1.5 linker list分析</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-1-6-u-boot-list-2-cmd-1%E6%80%8E%E4%B9%88%E6%9D%A5%E7%9A%84%EF%BC%9F"><span class="nav-text">5.1.1.6 u_boot_list_2_cmd_1怎么来的？</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-1-2-find-cmd-tbl"><span class="nav-text">5.1.2 find_cmd_tbl()</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-cmd-call"><span class="nav-text">5.2 cmd_call()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-cmd-never-repeatable"><span class="nav-text">5.3 cmd_never_repeatable()</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-%E6%80%BB%E7%BB%93%E4%B8%80%E4%B8%8B"><span class="nav-text">6. 总结一下</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/14f3979e.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV05-02-U-Boot-04-uboot下命令的执行 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV05-02-U-Boot-04-uboot下命令的执行
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2024-11-19 23:38:28" itemprop="dateCreated datePublished" datetime="2024-11-19T23:38:28+08:00">2024-11-19</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/" itemprop="url" rel="index"><span itemprop="name">02IMX6ULL平台</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/" itemprop="url" rel="index"><span itemprop="name">LV05-系统镜像</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>9.4k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>34 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>本文主要是uboot——uboot下命令的执行流程的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" rowspan="5">PC端开发环境</td>        <td align="center" width=150px>Windows</td>        <td align="left">Windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu20.04.2的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 17 Pro</td>        <td align="left">17.6.0 build-24238078</td>      </tr>    <tr>        <td align="center">终端软件</td>        <td align="left">MobaXterm(Professional Edition v23.0 Build 5042 (license))</td>    </tr>    <tr>        <td align="center">Win32DiskImager</td>        <td align="left">Win32DiskImager v1.0</td>      </tr>    <tr>        <td align="center" rowspan="3">Linux开发板环境</td>        <td align="center">Linux开发板</td>        <td align="left">正点原子 i.MX6ULL Linux 阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，使用的uboot版本为U-Boot 2019.04</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.19.71(NXP官方提供)</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="5">官方网站</td>        <td align="left"><a href="https://www.arm.com/" target="_blank">https://www.arm.com/</a></td>        <td align="left">ARM官方网站，在这里我们可以找到Cotex-Mx以及ARMVx的一些文档</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/" target="_blank">https://www.nxp.com.cn/ </a></td>        <td align="left">NXP官方网站</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxpic.org.cn/" target="_blank">https://www.nxpic.org.cn/</a></td><td align="left">NXP 官方社区</td>    </tr>    <tr>        <td align="left"><a href="https://u-boot.readthedocs.io/en/latest/" target="_blank">https://u-boot.readthedocs.io/en/latest/</a></td><td align="left">u-boot官网</td>    </tr>    <tr>        <td align="left"><a href="https://www.kernel.org/" target="_blank">https://www.kernel.org/</a></td><td align="left">linux内核官网</td>    </tr>    <tr>        <td align="center" rowspan="5">其他网站</td>        <td align="left"><a href="https://elixir.bootlin.com/linux/v4.15/source/kernel" target="_blank">kernel - Linux source code (v4.15) - Bootlin</a></td>        <td align="left">linux内核源码在线查看</td>    </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="3">NXP</td>        <td align="left"><a href="https://github.com/nxp-imx" target="_blank">https://github.com/nxp-imx</a></td>        <td align="left">NXP imx开发资源GitHub组织，里边会有u-boot和linux内核的仓库</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/linux-imx/releases/tag/v4.19.71" target="_blank">nxp-imx/linux-imx/releases/tag/v4.19.71</a></td>        <td align="left">NXP linux内核仓库tags中的v4.19.71</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0" target="_blank">nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0</a></td>        <td align="left">NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0</td>    </tr>    <tr>        <td align="center" rowspan="2">I.MX6ULL</td>        <td align="left"><a href="https://www.nxp.com.cn/docs/en/data-sheet/IMX6ULLIEC.pdf" target="_blank">i.MX 6ULL Applications Processors for Industrial Products</a></td>        <td align="left">I.MX6ULL 芯片手册（datasheet，可以在线查看）</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh" target="_blank">i.MX 6ULL Applications ProcessorReference Manual</a></td>        <td align="left">I.MX6ULL 参考手册（下载后才能查看，需要登录NXP官网）</td>    </tr>    <tr>        <td align="center" rowspan="2">Source Code</td>        <td align="left"><a href="https://elixir.bootlin.com/linux/latest/source" target="_blank">https://elixir.bootlin.com/linux/latest/source</a></td>        <td align="left">linux kernel源码</td>    </tr>    <tr>        <td align="left"><a href="https://elixir.bootlin.com/u-boot/latest/source" target="_blank">https://elixir.bootlin.com/u-boot/latest/source</a></td>        <td align="left">uboot源码</td>    </tr></table>
              </div>
            </details>

<p>我们进入uboot界面后敲命令就可以执行，我们从敲命令到按下enter键到执行是怎样的一个过程？uboot怎么识别到我们敲了什么命令，怎么执行的？这一节就来探讨一下吧。</p>
<blockquote>
<p>注意：本篇笔记从链接文件开始分析，但是不会详细分析，后面会专门学习uboot的启动流程，会详细的去分析uboot是怎么启动起来的。这里我们的目的是找到uboot的命令是怎么初始化的，怎么执行的。重点在于命令。</p>
</blockquote>
<h1 id="一、主循环在哪"><a href="#一、主循环在哪" class="headerlink" title="一、主循环在哪"></a><font size=3>一、主循环在哪</font></h1><p>我们知道uboot是一个大型的裸机程序，它不会说像linux系统一样有多个进程多个线程再执行，它只有一个进程，就是它自己。既然我们的程序能一直运行，那必然内部有一个循环在处理，这个大概推测一下就知道在循环中执行的肯定是uboot命令的解析和执行相关的部分，那么主循环在哪里？这一部分我们先来找一找主循环在哪里。</p>
<h2 id="1-寻找主循环函数"><a href="#1-寻找主循环函数" class="headerlink" title="1. 寻找主循环函数"></a><font size=3>1. 寻找主循环函数</font></h2><h3 id="1-1-main函数在哪？"><a href="#1-1-main函数在哪？" class="headerlink" title="1.1 main函数在哪？"></a><font size=3>1.1 main函数在哪？</font></h3><p>正常来说，我们看到一个程序一定是找main函数，因为在做裸机开发、应用开发的时候，我们主函数都是这样的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * argv[])</span>;</span><br></pre></td></tr></table></figure>

<p>那我们就按照这个思路来分析一下，一步一步找一下这个函数，看看它究竟在哪里。</p>
<h4 id="1-1-1-u-boot-lds"><a href="#1-1-1-u-boot-lds" class="headerlink" title="1.1.1 u-boot.lds"></a><font size=3>1.1.1 u-boot.lds</font></h4><p>我们知道uboot是一个大型的裸机程序，那么它一定有一个main函数，其实不管是不是裸机程序，都是有一个main函数的。这个main函数在哪？程序的链接是由链接脚本来决定的，所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接脚本为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/arch/arm/cpu/u-boot.lds">u-boot.lds - arch&#x2F;arm&#x2F;cpu&#x2F;u-boot.lds</a>。但是这个不是最终使用的链接脚本，最终的链接脚本是在这个链接脚本的基础上生成的。  我们编译完uboot就会在uboot源码目录下生成一个u-boot.lds（<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/01_uboot/01_gpio_cmd/u-boot.lds">01_uboot&#x2F;01_gpio_cmd&#x2F;u-boot.lds · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a>），我们打开看一下：</p>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">OUTPUT_FORMAT(&quot;elf32-littlearm&quot;, &quot;elf32-littlearm&quot;, &quot;elf32-littlearm&quot;)</span><br><span class="line">OUTPUT_ARCH(arm)</span><br><span class="line">ENTRY(_start)</span><br><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line">    . = 0x00000000;</span><br><span class="line">    . = ALIGN(4);</span><br><span class="line">    .text :</span><br><span class="line">    &#123;</span><br><span class="line">        *(.__image_copy_start)</span><br><span class="line">        *(.vectors)</span><br><span class="line">        arch/arm/cpu/armv7/start.o (.text*)</span><br><span class="line">    &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>说明: ENTRY(SYMBOL)：将符号 SYMBOL 的值设置为入口地址，入口地址是进程执行的第一条指令在进程地址空间的地址（<strong>比如 ENTRY(Reset_Handler) 表示进程最开始从复位中断服务函数处执行</strong>）</p>
</blockquote>
<p>在第三行中，有一个_start符号，这里就是代码的入口点，这个时候我们去源码里面搜的话，会有一堆，很多文件里都有。我们继续看往下看链接文件，</p>
<ul>
<li><p><code>SECTIONS</code>定义了段，包括<code>text</code>文本段、<code>data</code>数据段、<code>bss</code>段等。</p>
</li>
<li><p><code>__image_copy_start</code>在System.map和u-boot.map中均有定义，它是.text段的起始地址，我们可以搜索一下，就会发现它在System.map和u-boot.map中的值为0x87800000,也就是链接地址。</p>
</li>
<li><p><code>*(.vectors)</code> 包含所有 <code>.vectors</code> 段的内容，这通常用于存放中断向量等。</p>
</li>
<li><p><code>arch/arm/cpu/armv7/start.o</code>对应文件<code>arch/arm/cpu/armv7/start.S</code>，该文件中定义了<code>main</code>函数的入口。</p>
</li>
</ul>
<h4 id="1-1-2-vectors-S"><a href="#1-1-2-vectors-S" class="headerlink" title="1.1.2 vectors.S"></a><font size=3>1.1.2 vectors.S</font></h4><p>接下来我们先找中断向量表，因为前面学习裸机开发的时候我们程序一开始就是要设置异常向量表，那么中断向量表定义在哪个文件？我们用grep命令搜索一下<code>.vectors</code>这个关键词，这样搜索出来还是一堆文件，哪一个是我们要的？首先，它一定是一个汇编文件，其次，它一定含有入口点_start并且包含一些中断向量表的定义。我们就可以定位到这个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/arch/arm/lib/vectors.S#L45">vectors.S - arch&#x2F;arm&#x2F;lib&#x2F;vectors.S</a> 文件，下面的我精简了一下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">        .macro ARM_VECTORS</span><br><span class="line">#ifdef CONFIG_ARCH_K3</span><br><span class="line">	ldr     pc, _reset</span><br><span class="line">#else</span><br><span class="line">	b	reset</span><br><span class="line">#endif</span><br><span class="line">	ldr	pc, _undefined_instruction</span><br><span class="line">	ldr	pc, _software_interrupt</span><br><span class="line">	ldr	pc, _prefetch_abort</span><br><span class="line">	ldr	pc, _data_abort</span><br><span class="line">	ldr	pc, _not_used</span><br><span class="line">	ldr	pc, _irq</span><br><span class="line">	ldr	pc, _fiq</span><br><span class="line">	.endm</span><br><span class="line"></span><br><span class="line">.globl _start</span><br><span class="line"></span><br><span class="line">	.section &quot;.vectors&quot;, &quot;ax&quot;</span><br><span class="line">	/* ...... */</span><br><span class="line">_start:</span><br><span class="line">#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">	.word	CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">#endif</span><br><span class="line">	ARM_VECTORS</span><br><span class="line">#endif /* !defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) */</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>可以看到这里就是定义了中断向量表，并且一开始会跳转到reset中执行。reset函数在哪？我们继续分析。</p>
<h4 id="1-1-3-start-S"><a href="#1-1-3-start-S" class="headerlink" title="1.1.3  start.S"></a><font size=3>1.1.3  start.S</font></h4><p>reset这个符号并没有定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/arch/arm/lib/vectors.S">vectors.S - arch&#x2F;arm&#x2F;lib&#x2F;vectors.S</a>文件中，它定义在哪？从u-boot.lds中推测一下，它里面有这么一行：</p>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/cpu/armv7/start.o (.text*)</span><br></pre></td></tr></table></figure>

<p>多少肯定有点关系，这个start.o对应的文件应该就是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/arch/arm/cpu/armv7/start.S">start.S - arch&#x2F;arm&#x2F;cpu&#x2F;armv7&#x2F;start.S</a>，我们打开这个文件看一下</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">/* ... ... */</span><br><span class="line">.globl	reset</span><br><span class="line">.globl	save_boot_params_ret</span><br><span class="line">.type   save_boot_params_ret,%function</span><br><span class="line">   /* ... ... */</span><br><span class="line">bl	_main</span><br><span class="line">/* ... ... */</span><br><span class="line">ENTRY(c_runtime_cpu_setup)</span><br><span class="line">/* ... ... */</span><br><span class="line">   ENDPROC(c_runtime_cpu_setup)</span><br><span class="line">   /* ... ... */</span><br></pre></td></tr></table></figure>

<p>会发现猜想是对的，reset符号就在这里，这里会调用一些初始化函数，例如 lowlevel_init，这里我们暂时不关心，这里我们要做的是找到主循环所在的地方。简单看一下代码可以看到，到最后是跳转到_main函数执行了。</p>
<h4 id="1-1-4-crt0-S"><a href="#1-1-4-crt0-S" class="headerlink" title="1.1.4 crt0.S"></a><font size=3>1.1.4 crt0.S</font></h4><p>我们搜一下这个_main的符号在哪，会找到这个文件 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/arch/arm/lib/crt0.S#L64">crt0.S - arch&#x2F;arm&#x2F;lib&#x2F;crt0.S</a>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(_main)</span><br><span class="line">	/* ...... */</span><br><span class="line">	bic	r0, r0, #7	/* 8-byte alignment for ABI compliance */</span><br><span class="line">	mov	sp, r0</span><br><span class="line">	bl	board_init_f_alloc_reserve</span><br><span class="line">	mov	sp, r0</span><br><span class="line">	/* set up gd here, outside any C code */</span><br><span class="line">	mov	r9, r0</span><br><span class="line">	bl	board_init_f_init_reserve</span><br><span class="line"></span><br><span class="line">	mov	r0, #0</span><br><span class="line">	bl	board_init_f</span><br><span class="line">	/* ...... */</span><br><span class="line">	/* call board_init_r(gd_t *id, ulong dest_addr) */</span><br><span class="line">	mov     r0, r9                  /* gd_t */</span><br><span class="line">	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */</span><br><span class="line">	/* call board_init_r */</span><br><span class="line">#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)</span><br><span class="line">	ldr	lr, =board_init_r	/* this is auto-relocated! */</span><br><span class="line">	bx	lr</span><br><span class="line">#else</span><br><span class="line">	ldr	pc, =board_init_r	/* this is auto-relocated! */</span><br><span class="line">#endif</span><br><span class="line">	/* we should not return here. */</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">ENDPROC(_main)</span><br></pre></td></tr></table></figure>

<p>我们暂时不关心其他的函数，这里有两个函数值得我们关注</p>
<ul>
<li>board_init_f 函数主要有两个工作</li>
</ul>
<p>（1）初始化一系列外设，比如串口、定时器，或者打印一些消息等。</p>
<p>（2）初始化 gd 的各个成员变量，uboot 会将自己重定位到 DRAM 最后面的地址区域，也就 是将自己拷贝到 DRAM 最后面的内存区域中。</p>
<ul>
<li>board_init_r</li>
</ul>
<p>board_init_f 并没有初始化所有的外设，还需要做一些后续工作，这 些后续工作就是由函数 board_init_r 来完成的。后面继续分析就会知道我们要找的命令的初始化以及调用相关的东西都在这个函数中。</p>
<h3 id="1-2-board-init-r"><a href="#1-2-board-init-r" class="headerlink" title="1.2 board_init_r()"></a><font size=3>1.2 board_init_r()</font></h3><p>这个函数定义在哪？其实搜索一下，大概可以判断的出来，或者加点打印信息确认就可以知道这个函数定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/board_r.c#L830">board_r.c - common&#x2F;board_r.c</a>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">board_init_r</span><span class="params">(<span class="type">gd_t</span> *new_gd, ulong dest_addr)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Set up the new global data pointer. So far only x86 does this</span></span><br><span class="line"><span class="comment">	 * here.</span></span><br><span class="line"><span class="comment">	 * TODO(sjg@chromium.org): Consider doing this for all archs, or</span></span><br><span class="line"><span class="comment">	 * dropping the new_gd parameter.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> CONFIG_IS_ENABLED(X86_64)</span></span><br><span class="line">	arch_setup_gd(new_gd);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_NEEDS_MANUAL_RELOC</span></span><br><span class="line">	<span class="type">int</span> i;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(CONFIG_X86) &amp;&amp; !defined(CONFIG_ARM) &amp;&amp; !defined(CONFIG_ARM64)</span></span><br><span class="line">	gd = new_gd;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	gd-&gt;flags &amp;= ~GD_FLG_LOG_READY;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_NEEDS_MANUAL_RELOC</span></span><br><span class="line">	<span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; ARRAY_SIZE(init_sequence_r); i++)</span><br><span class="line">		init_sequence_r[i] += gd-&gt;reloc_off;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (initcall_run_list(init_sequence_r))</span><br><span class="line">		hang();</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* NOTREACHED - run_main_loop() does not return */</span></span><br><span class="line">	hang();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里面也没几行代码，我们直接看重点initcall_run_list。可以看一下最后一行的注释，就会发现，这个函数最终会一直运行在run_main_loop()函数中，不会再返回，也就是说这里就是最终一直死循环处理命令的函数了。</p>
<h3 id="1-3-initcall-run-list"><a href="#1-3-initcall-run-list" class="headerlink" title="1.3 initcall_run_list()"></a><font size=3>1.3 initcall_run_list()</font></h3><p>这个函数定义在 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/initcall.h#L17">initcall.h - include&#x2F;initcall.h</a> ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">int</span> <span class="title function_">initcall_run_list</span><span class="params">(<span class="type">const</span> <span class="type">init_fnc_t</span> init_sequence[])</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">init_fnc_t</span> *init_fnc_ptr;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) &#123;</span><br><span class="line">		<span class="type">unsigned</span> <span class="type">long</span> reloc_ofs = <span class="number">0</span>;</span><br><span class="line">		<span class="type">int</span> ret;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (gd-&gt;flags &amp; GD_FLG_RELOC)</span><br><span class="line">			reloc_ofs = gd-&gt;reloc_off;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_EFI_APP</span></span><br><span class="line">		reloc_ofs = (<span class="type">unsigned</span> <span class="type">long</span>)image_base;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		debug(<span class="string">&quot;initcall: %p&quot;</span>, (<span class="type">char</span> *)*init_fnc_ptr - reloc_ofs);</span><br><span class="line">		<span class="keyword">if</span> (gd-&gt;flags &amp; GD_FLG_RELOC)</span><br><span class="line">			debug(<span class="string">&quot; (relocated to %p)\n&quot;</span>, (<span class="type">char</span> *)*init_fnc_ptr);</span><br><span class="line">		<span class="keyword">else</span></span><br><span class="line">			debug(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">		ret = (*init_fnc_ptr)();</span><br><span class="line">		<span class="keyword">if</span> (ret) &#123;</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">&quot;initcall sequence %p failed at call %p (err=%d)\n&quot;</span>,</span><br><span class="line">			       init_sequence,</span><br><span class="line">			       (<span class="type">char</span> *)*init_fnc_ptr - reloc_ofs, ret);</span><br><span class="line">			<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>简化一下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">int</span> <span class="title function_">initcall_run_list</span><span class="params">(<span class="type">const</span> <span class="type">init_fnc_t</span> init_sequence[])</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">init_fnc_t</span> *init_fnc_ptr;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) &#123;</span><br><span class="line">		<span class="comment">/* ...... */</span></span><br><span class="line">		ret = (*init_fnc_ptr)();</span><br><span class="line">		<span class="keyword">if</span> (ret) &#123;</span><br><span class="line">			<span class="built_in">printf</span>(<span class="string">&quot;initcall sequence %p failed at call %p (err=%d)\n&quot;</span>,</span><br><span class="line">			       init_sequence,</span><br><span class="line">			       (<span class="type">char</span> *)*init_fnc_ptr - reloc_ofs, ret);</span><br><span class="line">			<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>先看一下init_fnc_t这个类型，它同样定义在 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/initcall.h#L9">initcall.h - include&#x2F;initcall.h </a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="title function_">int</span> <span class="params">(*<span class="type">init_fnc_t</span>)</span><span class="params">(<span class="type">void</span>)</span>;</span><br></pre></td></tr></table></figure>

<p>可以看到这是一个指向没有形参且返回值为int类型的函数的函数指针，所以上面的代码大概分析一下就是：</p>
<p>（1）定义了一个函数指针init_fnc_ptr，指向一个名为init_sequence的数组，这个数组里面每一个成员都是函数指针。</p>
<p>（2）便利函数指针数组init_sequence，然后执行。这个init_sequence是谁？我们看上一级调用就知道这个传入的参数是init_sequence_r。</p>
<h3 id="1-4-init-sequence-r"><a href="#1-4-init-sequence-r" class="headerlink" title="1.4 init_sequence_r"></a><font size=3>1.4 init_sequence_r</font></h3><p>init_sequence_r是一个函数指针数组，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/board_r.c#L641">board_r.c - common&#x2F;board_r.c </a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">init_fnc_t</span> init_sequence_r[] = &#123;</span><br><span class="line">	<span class="comment">/* ...... */</span></span><br><span class="line">	run_main_loop,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>其他的我们都先不看其他的，那些都是一些初始化，命令的初始化以及执行这些都在最后的run_main_loop函数中。</p>
<h2 id="2-run-main-loop"><a href="#2-run-main-loop" class="headerlink" title="2. run_main_loop()"></a><font size=3>2. run_main_loop()</font></h2><p>上面我们已经找到了这个函数被调用的地方，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/board_r.c#L624">board_r.c - common&#x2F;board_r.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SANDBOX</span></span><br><span class="line">	sandbox_main_loop_init();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* main_loop() can return to retry autoboot, if so just run it again */</span></span><br><span class="line">	<span class="keyword">for</span> (;;)</span><br><span class="line">		main_loop();</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到这里面是一个死循环了，一直在执行main_loop函数。所以，这里其实就是我们要找的主循环函数。</p>
<h2 id="3-main-loop"><a href="#3-main-loop" class="headerlink" title="3. main_loop"></a><font size=3>3. main_loop</font></h2><p>我们再来看一下main_loop这个函数，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/main.c#L41">main.c - common&#x2F;main.c</a>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* We come here after U-Boot is initialised and ready to process commands */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">char</span> *s;</span><br><span class="line"></span><br><span class="line">	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, <span class="string">&quot;main_loop&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (IS_ENABLED(CONFIG_VERSION_VARIABLE))</span><br><span class="line">		env_set(<span class="string">&quot;ver&quot;</span>, version_string);  <span class="comment">/* set version variable */</span></span><br><span class="line"></span><br><span class="line">	cli_init();</span><br><span class="line"></span><br><span class="line">	run_preboot_environment_command();</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (IS_ENABLED(CONFIG_UPDATE_TFTP))</span><br><span class="line">		update_tftp(<span class="number">0UL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">	s = bootdelay_process();</span><br><span class="line">	<span class="keyword">if</span> (cli_process_fdt(&amp;s))</span><br><span class="line">		cli_secure_boot_cmd(s);</span><br><span class="line"></span><br><span class="line">	autoboot_command(s);</span><br><span class="line"></span><br><span class="line">	cli_loop();</span><br><span class="line">	panic(<span class="string">&quot;No CLI available&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="4-总结一下"><a href="#4-总结一下" class="headerlink" title="4. 总结一下"></a><font size=3>4. 总结一下</font></h2><p>到这里我们就找到了主循环所在的函数，调用关系大概就是：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116075502350.png" alt="image-20241116075502350" />



<p>最后调用的main_loop()就是最后主循环的函数。</p>
<h1 id="二、main-loop在做什么？"><a href="#二、main-loop在做什么？" class="headerlink" title="二、main_loop在做什么？"></a><font size=3>二、main_loop在做什么？</font></h1><p>这里只分析命令相关的东西，其他的就暂时先不管。</p>
<h2 id="1-main-loop"><a href="#1-main-loop" class="headerlink" title="1. main_loop()"></a><font size=3>1. main_loop()</font></h2><p>main_loop函数定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/main.c#L41">main.c - common&#x2F;main.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* We come here after U-Boot is initialised and ready to process commands */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">char</span> *s;</span><br><span class="line"></span><br><span class="line">	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, <span class="string">&quot;main_loop&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (IS_ENABLED(CONFIG_VERSION_VARIABLE))</span><br><span class="line">		env_set(<span class="string">&quot;ver&quot;</span>, version_string);  <span class="comment">/* set version variable */</span></span><br><span class="line"></span><br><span class="line">	cli_init();</span><br><span class="line"></span><br><span class="line">	run_preboot_environment_command();</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (IS_ENABLED(CONFIG_UPDATE_TFTP))</span><br><span class="line">		update_tftp(<span class="number">0UL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">	s = bootdelay_process();</span><br><span class="line">	<span class="keyword">if</span> (cli_process_fdt(&amp;s))</span><br><span class="line">		cli_secure_boot_cmd(s);</span><br><span class="line"></span><br><span class="line">	autoboot_command(s);</span><br><span class="line"></span><br><span class="line">	cli_loop();</span><br><span class="line">	panic(<span class="string">&quot;No CLI available&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>我们先大概分析一下这个函数：</p>
<p>（1）调用 bootstage_mark_name() 函数，打印启动进度。  </p>
<p>（2）如果定义了宏 CONFIG_VERSION_VARIABLE 的话就会执行函数 setenv，设置环境变量 ver 的值为 version_string，也就是设置版本号环境变量。   </p>
<p>（3）cli_init() 函数，跟命令初始化有关，初始化 hush shell 相关的变量。  </p>
<p>（4）run_preboot_environment_command() 函数，获取环境变量 perboot 的内容， perboot是一些预启动命令，一般不使用这个环境变量。  </p>
<p>（5）CONFIG_UPDATE_TFTP这个宏我们搜索一下就会发现它是没有定义的，所以这里不用管。</p>
<p>（6）bootdelay_process 函数，此函数会读取环境变量 bootdelay 和 bootcmd 的内容，然后将 bootdelay 的值赋值给全局变量 stored_bootdelay，返回值为环境变量 bootcmd 的值。  </p>
<p>（7）cli_process_fdt()这里其实就是看这个CONFIG_OF_CONTROL有没有定义，如果定义了 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 就会实现，如果没有定义 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 直接返回一个 false。在本 uboot 中<br>没有定义 CONFIG_OF_CONTROL，因此 cli_process_fdt 函数返回值为 false。  所以这里也不管。</p>
<p>（8）autoboot_command() 函数，此函数就是检查倒计时是否结束？倒计时结束之前有没有被打断？  这里就不展开分析了，后面学习uboot启动流程的时候会详细去分析这个函数。</p>
<p>（9）cli_loop() 函数是 uboot 的命令行处理函数，我们在 uboot 中输入各种命令，进行各种操作就是 cli_loop() 来处理的 。所以后面我们重点看一下这个函数。</p>
<h2 id="2-cli-loop"><a href="#2-cli-loop" class="headerlink" title="2. cli_loop()"></a><font size=3>2. cli_loop()</font></h2><p>我们来看一下cli_loop()这个函数，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/cli.c#L214">cli.c - common&#x2F;cli.c</a>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">cli_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_HUSH_PARSER</span></span><br><span class="line">	parse_file_outer();</span><br><span class="line">	<span class="comment">/* This point is never reached */</span></span><br><span class="line">	<span class="keyword">for</span> (;;);</span><br><span class="line"><span class="meta">#<span class="keyword">elif</span> defined(CONFIG_CMDLINE)</span></span><br><span class="line">	cli_simple_loop();</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">&quot;## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n&quot;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_HUSH_PARSER*/</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里面有两个宏，我们找一找这些宏的定义，没有的就直接去掉，最后简化一下函数就是：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// CONFIG_HUSH_PARSER存在所以简化一下就是：</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">cli_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	parse_file_outer();</span><br><span class="line">	<span class="comment">/* This point is never reached */</span></span><br><span class="line">	<span class="keyword">for</span> (;;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>接下来我们继续看parse_file_outer();</p>
<h2 id="3-parse-file-outer"><a href="#3-parse-file-outer" class="headerlink" title="3. parse_file_outer()"></a><font size=3>3. parse_file_outer()</font></h2><p>parse_file_outer()这个函数定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/cli_hush.c#L3287">cli_hush.c - common&#x2F;cli_hush.c</a>，里面还是一些宏，我们直接根据宏是否定义把函数简化一下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">parse_file_outer</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> rcode;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">in_str</span> <span class="title">input</span>;</span></span><br><span class="line">    </span><br><span class="line">	setup_file_in_str(&amp;input);</span><br><span class="line">	rcode = parse_stream_outer(&amp;input, FLAG_PARSE_SEMICOLON);</span><br><span class="line">	<span class="keyword">return</span> rcode;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 6 行调用函数 setup_file_in_str() 初始化变量 input 的成员变量。</p>
<p>第 7 行调用函数 parse_stream_outer()，这个函数就是 hush shell 的命令解释器，负责接收命令行输入，然后解析并执行相应的命令。</p>
<h2 id="4-parse-stream-outer"><a href="#4-parse-stream-outer" class="headerlink" title="4. parse_stream_outer()"></a><font size=3>4. parse_stream_outer()</font></h2><p>接下来肯定是parse_stream_outer()这个函数了，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/cli_hush.c#L3184">cli_hush.c - common&#x2F;cli_hush.c</a>，这里还是一样，把里面的宏都去掉，简化后如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* most recursion does not come through here, the exeception is</span></span><br><span class="line"><span class="comment"> * from builtin_source() */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">parse_stream_outer</span><span class="params">(<span class="keyword">struct</span> in_str *inp, <span class="type">int</span> flag)</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">p_context</span> <span class="title">ctx</span>;</span></span><br><span class="line">	o_string temp=NULL_O_STRING;</span><br><span class="line">	<span class="type">int</span> rcode;</span><br><span class="line">	<span class="type">int</span> code = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">do</span> &#123;</span><br><span class="line">		ctx.type = flag;</span><br><span class="line">		initialize_context(&amp;ctx);</span><br><span class="line">		update_ifs_map();</span><br><span class="line">		<span class="keyword">if</span> (!(flag &amp; FLAG_PARSE_SEMICOLON) || (flag &amp; FLAG_REPARSING)) mapset((uchar *)<span class="string">&quot;;$&amp;|&quot;</span>, <span class="number">0</span>);</span><br><span class="line">		inp-&gt;promptmode=<span class="number">1</span>;</span><br><span class="line">		rcode = parse_stream(&amp;temp, &amp;ctx, inp, flag &amp; FLAG_CONT_ON_NEWLINE ? <span class="number">-1</span> : <span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (rcode == <span class="number">1</span>) flag_repeat = <span class="number">0</span>;</span><br><span class="line">		<span class="keyword">if</span> (rcode != <span class="number">1</span> &amp;&amp; ctx.old_flag != <span class="number">0</span>) &#123;</span><br><span class="line">			syntax();</span><br><span class="line">			flag_repeat = <span class="number">0</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (rcode != <span class="number">1</span> &amp;&amp; ctx.old_flag == <span class="number">0</span>) &#123;</span><br><span class="line">			done_word(&amp;temp, &amp;ctx);</span><br><span class="line">			done_pipe(&amp;ctx,PIPE_SEQ);</span><br><span class="line"></span><br><span class="line">			code = run_list(ctx.list_head);</span><br><span class="line">			<span class="keyword">if</span> (code == <span class="number">-2</span>) &#123;	<span class="comment">/* exit */</span></span><br><span class="line">				b_free(&amp;temp);</span><br><span class="line">				code = <span class="number">0</span>;</span><br><span class="line">				<span class="comment">/* XXX hackish way to not allow exit from main loop */</span></span><br><span class="line">				<span class="keyword">if</span> (inp-&gt;peek == file_peek) &#123;</span><br><span class="line">					<span class="built_in">printf</span>(<span class="string">&quot;exit not allowed from main input shell.\n&quot;</span>);</span><br><span class="line">					<span class="keyword">continue</span>;</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (code == <span class="number">-1</span>)</span><br><span class="line">			    flag_repeat = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (ctx.old_flag != <span class="number">0</span>) &#123;</span><br><span class="line">				<span class="built_in">free</span>(ctx.<span class="built_in">stack</span>);</span><br><span class="line">				b_reset(&amp;temp);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (inp-&gt;__promptme == <span class="number">0</span>) <span class="built_in">printf</span>(<span class="string">&quot;&lt;INTERRUPT&gt;\n&quot;</span>);</span><br><span class="line">			inp-&gt;__promptme = <span class="number">1</span>;</span><br><span class="line">			temp.nonnull = <span class="number">0</span>;</span><br><span class="line">			temp.quote = <span class="number">0</span>;</span><br><span class="line">			inp-&gt;p = <span class="literal">NULL</span>;</span><br><span class="line">			free_pipe_list(ctx.list_head,<span class="number">0</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		b_free(&amp;temp);</span><br><span class="line">	<span class="comment">/* loop on syntax errors, return on EOF */</span></span><br><span class="line">	&#125; <span class="keyword">while</span> (rcode != <span class="number">-1</span> &amp;&amp; !(flag &amp; FLAG_EXIT_FROM_LOOP) &amp;&amp;</span><br><span class="line">		(inp-&gt;peek != static_peek || b_peek(inp)));</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> (code != <span class="number">0</span>) ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 11 ~ 56 行中的 do-while 循环就是处理输入命令的。 这里的命令解析什么的我都没有仔细去研究了，内部大概是这样一个调用关系：</p>
 <img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116133256624.png" alt="image-20241116133256624" />

<h2 id="5-cmd-process"><a href="#5-cmd-process" class="headerlink" title="5. cmd_process()"></a><font size=3>5. cmd_process()</font></h2><p>前面分析过了，到这个cmd_process()函数，要经过：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">parse_stream_outer() </span><br><span class="line">    --&gt; run_list</span><br><span class="line">    	--&gt; run_list_real()</span><br><span class="line">    		--&gt; run_pipe_real()</span><br><span class="line">    			--&gt; cmd_process()</span><br></pre></td></tr></table></figure>

<p>中间那几个函数这里就不管了，我看了下好像还挺复杂的，以后有机会再深入分析吧。我们看一下cmd_process()，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/command.c#L571">command.c - common&#x2F;command.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="type">command_ret_t</span> <span class="title function_">cmd_process</span><span class="params">(<span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params">			       <span class="type">int</span> *repeatable, ulong *ticks)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">enum</span> <span class="title">command_ret_t</span> <span class="title">rc</span> =</span> CMD_RET_SUCCESS;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *cmdtp;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Look up command in command table */</span></span><br><span class="line">	cmdtp = find_cmd(argv[<span class="number">0</span>]);</span><br><span class="line">	<span class="keyword">if</span> (cmdtp == <span class="literal">NULL</span>) &#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;Unknown command &#x27;%s&#x27; - try &#x27;help&#x27;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* found - check max args */</span></span><br><span class="line">	<span class="keyword">if</span> (argc &gt; cmdtp-&gt;maxargs)</span><br><span class="line">		rc = CMD_RET_USAGE;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_CMD_BOOTD)</span></span><br><span class="line">	<span class="comment">/* avoid &quot;bootd&quot; recursion */</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (cmdtp-&gt;cmd == do_bootd) &#123;</span><br><span class="line">		<span class="keyword">if</span> (flag &amp; CMD_FLAG_BOOTD) &#123;</span><br><span class="line">			<span class="built_in">puts</span>(<span class="string">&quot;&#x27;bootd&#x27; recursion detected\n&quot;</span>);</span><br><span class="line">			rc = CMD_RET_FAILURE;</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			flag |= CMD_FLAG_BOOTD;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* If OK so far, then do the command */</span></span><br><span class="line">	<span class="keyword">if</span> (!rc) &#123;</span><br><span class="line">		<span class="type">int</span> newrep;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(<span class="number">0</span>);</span><br><span class="line">		rc = cmd_call(cmdtp, flag, argc, argv, &amp;newrep);</span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(*ticks);</span><br><span class="line">		*repeatable &amp;= newrep;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (rc == CMD_RET_USAGE)</span><br><span class="line">		rc = cmd_usage(cmdtp);</span><br><span class="line">	<span class="keyword">return</span> rc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们查一下用到的宏都有没有定义，然后把宏都去掉，简化一下，但是发现宏是定义了的，所以简化不了了那我们一个一个看。</p>
<h3 id="5-1-find-cmd"><a href="#5-1-find-cmd" class="headerlink" title="5.1 find_cmd()"></a><font size=3>5.1 find_cmd()</font></h3><p>从名字上看就知道这个是查找命令的，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/command.c#L119">command.c - common&#x2F;command.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> *<span class="title function_">find_cmd</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cmd)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *start = ll_entry_start(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="type">const</span> <span class="type">int</span> len = ll_entry_count(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="keyword">return</span> find_cmd_tbl(cmd, start, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到传入的参数是一个字符串类型，由于前面一大部分的调用我们都没分析，这里我们可以加条打印信息，还是以前面的gpio命令为例，我们看一下这里传进来的是什么：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> *<span class="title function_">find_cmd</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cmd)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s:%d cmd=%s\n&quot;</span>, __FUNCTION__, __LINE__, cmd);</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *start = ll_entry_start(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="type">const</span> <span class="type">int</span> len = ll_entry_count(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="keyword">return</span> find_cmd_tbl(cmd, start, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后编译烧写到sd卡并启动，我们在uboot的命令模式下敲以下命令：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=&gt; gpio toggle GPIO1_3</span><br></pre></td></tr></table></figure>

<p>可以看到有如下打印信息：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116165331445.png" alt="image-20241116165331445" />

<p>可以看到这里收到的字符串就是敲的gpio关键词。</p>
<h4 id="5-1-1-linker-lists"><a href="#5-1-1-linker-lists" class="headerlink" title="5.1.1 linker_lists"></a><font size=3>5.1.1 linker_lists</font></h4><p>我们先来了解一下<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/Documentation/linker_lists.rst">linker_lists</a>这部分相关的几个宏定义。</p>
<h5 id="5-1-1-1-ll-entry-start"><a href="#5-1-1-1-ll-entry-start" class="headerlink" title="5.1.1.1 ll_entry_start"></a><font size=3>5.1.1.1 ll_entry_start</font></h5><p>我们来看一下这个 ll_entry_start 在干什么，它不是一个函数，而是一个宏，定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/linker_lists.h#L125">linker_lists.h - include&#x2F;linker_lists.h</a></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * We need a 0-byte-size type for iterator symbols, and the compiler</span></span><br><span class="line"><span class="comment"> * does not allow defining objects of C type &#x27;void&#x27;. Using an empty</span></span><br><span class="line"><span class="comment"> * struct is allowed by the compiler, but causes gcc versions 4.4 and</span></span><br><span class="line"><span class="comment"> * below to complain about aliasing. Therefore we use the next best</span></span><br><span class="line"><span class="comment"> * thing: zero-sized arrays, which are both 0-byte-size and exempt from</span></span><br><span class="line"><span class="comment"> * aliasing warnings.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ll_entry_start() - Point to first entry of linker-generated array</span></span><br><span class="line"><span class="comment"> * @_type:	Data type of the entry</span></span><br><span class="line"><span class="comment"> * @_list:	Name of the list in which this entry is placed</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This function returns ``(_type *)`` pointer to the very first entry of a</span></span><br><span class="line"><span class="comment"> * linker-generated array placed into subsection of .u_boot_list section</span></span><br><span class="line"><span class="comment"> * specified by _list argument.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Since this macro defines an array start symbol, its leftmost index</span></span><br><span class="line"><span class="comment"> * must be 2 and its rightmost index must be 1.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Example:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * ::</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub);</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p>这个宏就是声明一个指向_type类型的_list数组中第一个元素的类型为_type指针。所以这也就意味着，我们可以通过这个指针获取到_list这个数组的首地址。这个宏我们展开一下吧，在find_cmd()函数中是这样使用的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> *start = ll_entry_start(<span class="type">cmd_tbl_t</span>, cmd);</span><br></pre></td></tr></table></figure>

<p>那我们展开它：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 先把参数替换一下</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(cmd_tbl_t, cmd)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span><span class="string">&quot;cmd&quot;</span><span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(cmd_tbl_t *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"><span class="comment">// 然后展开得到</span></span><br><span class="line"><span class="type">cmd_tbl_t</span> *start = &#123;</span><br><span class="line">	<span class="type">static</span> <span class="type">char</span> start[<span class="number">0</span>] __aligned(<span class="number">4</span>) __attribute__((unused, section(<span class="string">&quot;.u_boot_list_2_&quot;</span><span class="string">&quot;cmd&quot;</span><span class="string">&quot;_1&quot;</span>)));		</span><br><span class="line">	(<span class="type">cmd_tbl_t</span> *)&amp;start;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以gpio命令为例（这里先埋个坑），这里就是：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这是一种错误的做法</span></span><br><span class="line"><span class="type">cmd_tbl_t</span> *start = &#123;</span><br><span class="line">	<span class="type">static</span> <span class="type">char</span> start[<span class="number">0</span>] __aligned(<span class="number">4</span>) __attribute__((unused, section(<span class="string">&quot;.u_boot_list_2_gpio_1&quot;</span>)));		</span><br><span class="line">	(<span class="type">cmd_tbl_t</span> *)&amp;start;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但是呢，这里不能这么想，这个<code>.u_boot_list_2_gpio_1</code>在映射文件中是没有的，我们也找不到这个段，那这里应该怎么搞？我后来在这个宏里面加了打印：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">    printf(<span class="string">&quot;%s:%d ll_entry_start %s\n&quot;</span>, __FUNCTION__, __LINE__, #_list);\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116165716871.png" alt="image-20241116165716871" />

<p>为啥？？？？？？？其实这里是一个宏，我们是要在预处理阶段就展开的，所以这里我们把这个先展开，不能说是把形参直接替换进去，所以这里展开的过程是对的，就是：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);</span></span><br><span class="line"></span><br><span class="line"><span class="type">cmd_tbl_t</span> *start = &#123;</span><br><span class="line">	<span class="type">static</span> <span class="type">char</span> start[<span class="number">0</span>] __aligned(<span class="number">4</span>) __attribute__((unused, section(<span class="string">&quot;.u_boot_list_2_&quot;</span><span class="string">&quot;cmd&quot;</span><span class="string">&quot;_1&quot;</span>)));		</span><br><span class="line">	(<span class="type">cmd_tbl_t</span> *)&amp;start;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以，这个find_cmd()函数就变成了：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> *<span class="title function_">find_cmd</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cmd)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *start = &#123;</span><br><span class="line">        <span class="type">static</span> <span class="type">char</span> start[<span class="number">0</span>] __aligned(<span class="number">4</span>) __attribute__((unused, section(<span class="string">&quot;.u_boot_list_2_cmd_1&quot;</span>)));	</span><br><span class="line">        (<span class="type">cmd_tbl_t</span> *)&amp;start;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="type">const</span> <span class="type">int</span> len = ll_entry_count(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="keyword">return</span> find_cmd_tbl(cmd, start, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个样子才对。</p>
<h5 id="5-1-1-2-ll-entry-end"><a href="#5-1-1-2-ll-entry-end" class="headerlink" title="5.1.1.2 ll_entry_end"></a><font size=3>5.1.1.2 ll_entry_end</font></h5><p>我们直接看一下ll_entry_end这个宏，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/linker_lists.h#L151">linker_lists.h - include&#x2F;linker_lists.h</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ll_entry_end() - Point after last entry of linker-generated array</span></span><br><span class="line"><span class="comment"> * @_type:	Data type of the entry</span></span><br><span class="line"><span class="comment"> * @_list:	Name of the list in which this entry is placed</span></span><br><span class="line"><span class="comment"> *		(with underscores instead of dots)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This function returns ``(_type *)`` pointer after the very last entry of</span></span><br><span class="line"><span class="comment"> * a linker-generated array placed into subsection of .u_boot_list</span></span><br><span class="line"><span class="comment"> * section specified by _list argument.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Since this macro defines an array end symbol, its leftmost index</span></span><br><span class="line"><span class="comment"> * must be 2 and its rightmost index must be 3.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Example:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * ::</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   struct my_sub_cmd *msc = ll_entry_end(struct my_sub_cmd, cmd_sub);</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_end(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char end[0] __aligned(4) __attribute__((unused,		\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_3&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;end;							\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p>声明一个指向_type类型的_list数组中最后一个元素的末尾地址的下一个地址的类型为_type指针。</p>
<h5 id="5-1-1-3-ll-entry-count"><a href="#5-1-1-3-ll-entry-count" class="headerlink" title="5.1.1.3 ll_entry_count"></a><font size=3>5.1.1.3 ll_entry_count</font></h5><p>我们接着来看这个ll_entry_count，它也是一个宏，定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/linker_lists.h#L176">linker_lists.h - include&#x2F;linker_lists.h</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ll_entry_count() - Return the number of elements in linker-generated array</span></span><br><span class="line"><span class="comment"> * @_type:	Data type of the entry</span></span><br><span class="line"><span class="comment"> * @_list:	Name of the list of which the number of elements is computed</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This function returns the number of elements of a linker-generated array</span></span><br><span class="line"><span class="comment"> * placed into subsection of .u_boot_list section specified by _list</span></span><br><span class="line"><span class="comment"> * argument. The result is of an unsigned int type.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Example:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * ::</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   int i;</span></span><br><span class="line"><span class="comment"> *   const unsigned int count = ll_entry_count(struct my_sub_cmd, cmd_sub);</span></span><br><span class="line"><span class="comment"> *   struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub);</span></span><br><span class="line"><span class="comment"> *   for (i = 0; i &lt; count; i++, msc++)</span></span><br><span class="line"><span class="comment"> *           printf(&quot;Entry %i, x=%i y=%i\n&quot;, i, msc-&gt;x, msc-&gt;y);</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_count(_type, _list)					\</span></span><br><span class="line"><span class="meta">	(&#123;								\</span></span><br><span class="line"><span class="meta">		_type *start = ll_entry_start(_type, _list);		\</span></span><br><span class="line"><span class="meta">		_type *end = ll_entry_end(_type, _list);		\</span></span><br><span class="line"><span class="meta">		unsigned int _ll_result = end - start;			\</span></span><br><span class="line"><span class="meta">		_ll_result;						\</span></span><br><span class="line"><span class="meta">	&#125;)</span></span><br></pre></td></tr></table></figure>

<p>这个宏是返回_type类型的_list数组中元素个数。前面已经找到了\list数组的起始地址ll_entry_start和结束地址ll_entry_end，两者相减就是中间元素的个数。</p>
<h5 id="5-1-1-4-ll-entry-declare"><a href="#5-1-1-4-ll-entry-declare" class="headerlink" title="5.1.1.4 ll_entry_declare"></a><font size=3>5.1.1.4 ll_entry_declare</font></h5><p>前面我们分析gpio命令声明的时候有展开过它，它是定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/linker_lists.h#L70">linker_lists.h - include&#x2F;linker_lists.h</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ll_entry_declare() - Declare linker-generated array entry</span></span><br><span class="line"><span class="comment"> * @_type:	Data type of the entry</span></span><br><span class="line"><span class="comment"> * @_name:	Name of the entry</span></span><br><span class="line"><span class="comment"> * @_list:	name of the list. Should contain only characters allowed</span></span><br><span class="line"><span class="comment"> *		in a C variable name!</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This macro declares a variable that is placed into a linker-generated</span></span><br><span class="line"><span class="comment"> * array. This is a basic building block for more advanced use of linker-</span></span><br><span class="line"><span class="comment"> * generated arrays. The user is expected to build their own macro wrapper</span></span><br><span class="line"><span class="comment"> * around this one.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * A variable declared using this macro must be compile-time initialized.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Special precaution must be made when using this macro:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 1) The _type must not contain the &quot;static&quot; keyword, otherwise the</span></span><br><span class="line"><span class="comment"> *    entry is generated and can be iterated but is listed in the map</span></span><br><span class="line"><span class="comment"> *    file and cannot be retrieved by name.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 2) In case a section is declared that contains some array elements AND</span></span><br><span class="line"><span class="comment"> *    a subsection of this section is declared and contains some elements,</span></span><br><span class="line"><span class="comment"> *    it is imperative that the elements are of the same type.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 3) In case an outer section is declared that contains some array elements</span></span><br><span class="line"><span class="comment"> *    AND an inner subsection of this section is declared and contains some</span></span><br><span class="line"><span class="comment"> *    elements, then when traversing the outer section, even the elements of</span></span><br><span class="line"><span class="comment"> *    the inner sections are present in the array.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Example:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * ::</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   ll_entry_declare(struct my_sub_cmd, my_sub_cmd, cmd_sub) = &#123;</span></span><br><span class="line"><span class="comment"> *           .x = 3,</span></span><br><span class="line"><span class="comment"> *           .y = 4,</span></span><br><span class="line"><span class="comment"> *   &#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_declare(_type, _name, _list)				\</span></span><br><span class="line"><span class="meta">	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\</span></span><br><span class="line"><span class="meta">			__attribute__((unused,				\</span></span><br><span class="line"><span class="meta">			section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_2_&quot;</span>#_name)))</span></span><br></pre></td></tr></table></figure>

<p>在_list数组中声明一个链接器产生的_type类型命名为name的元素。</p>
<h5 id="5-1-1-5-linker-list分析"><a href="#5-1-1-5-linker-list分析" class="headerlink" title="5.1.1.5 linker list分析"></a><font size=3>5.1.1.5 linker list分析</font></h5><p>这一部分我们可以参考<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/Documentation/linker_lists.rst">linker_lists.rst - Documentation&#x2F;linker_lists.rst</a>或者我的uboot的源码仓库<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/u-boot/blob/master/doc/linker_lists.rst">doc&#x2F;linker_lists.rst · 苏木&#x2F;u-boot - 码云 - 开源中国 (gitee.com)</a>。</p>
<p>上面我们接触到了四个宏：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_end(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char end[0] __aligned(4) __attribute__((unused,		\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_3&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;end;							\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_count(_type, _list)					\</span></span><br><span class="line"><span class="meta">	(&#123;								\</span></span><br><span class="line"><span class="meta">		_type *start = ll_entry_start(_type, _list);		\</span></span><br><span class="line"><span class="meta">		_type *end = ll_entry_end(_type, _list);		\</span></span><br><span class="line"><span class="meta">		unsigned int _ll_result = end - start;			\</span></span><br><span class="line"><span class="meta">		_ll_result;						\</span></span><br><span class="line"><span class="meta">	&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_declare(_type, _name, _list)				\</span></span><br><span class="line"><span class="meta">	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\</span></span><br><span class="line"><span class="meta">			__attribute__((unused,				\</span></span><br><span class="line"><span class="meta">			section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_2_&quot;</span>#_name)))</span></span><br></pre></td></tr></table></figure>

<p>为了计算数组中元素的个数定义了ll_entry_start和ll_entry_end两个宏，在link_list数据结构中段的命名都是以<code>.u_boot_list_2_</code>开始，<code>.u_boot_list_2_&quot;#_list&quot;_2_&quot;#_name</code>中_list为数组项名称，_name数组项下的元素名称，我们其实可以先去u-boot.map文件中看一下uboot中都定义了哪些数组（<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/01_uboot/01_gpio_cmd/u-boot.map">01_uboot&#x2F;01_gpio_cmd&#x2F;u-boot.map · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国 (gitee.com)</a>）,这里列举两部分（这里应该可以理解为数组）：</p>
<ul>
<li><code>.u_boot_list_2_cmd</code></li>
</ul>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">.u_boot_list_2_cmd_1</span><br><span class="line">               0x00000000878899b4        0x0 cmd/built-in.o</span><br><span class="line">.u_boot_list_2_cmd_1</span><br><span class="line">               0x00000000878899b4        0x0 common/built-in.o</span><br><span class="line">.u_boot_list_2_cmd_2_base</span><br><span class="line">               0x00000000878899b4       0x1c cmd/built-in.o</span><br><span class="line">               0x00000000878899b4                _u_boot_list_2_cmd_2_base</span><br><span class="line">/*中间省略*/</span><br><span class="line">.u_boot_list_2_cmd_2_gpio</span><br><span class="line">               0x0000000087889ed8       0x1c cmd/built-in.o</span><br><span class="line">               0x0000000087889ed8                _u_boot_list_2_cmd_2_gpio</span><br><span class="line">/*中间省略*/</span><br><span class="line">.u_boot_list_2_cmd_2_mmc</span><br><span class="line">               0x000000008788a0ec       0x1c cmd/built-in.o</span><br><span class="line">               0x000000008788a0ec                _u_boot_list_2_cmd_2_mmc</span><br><span class="line"> /*中间省略*/</span><br><span class="line">.u_boot_list_2_cmd_2_version</span><br><span class="line">               0x000000008788a3fc       0x1c cmd/built-in.o</span><br><span class="line">               0x000000008788a3fc                _u_boot_list_2_cmd_2_version</span><br><span class="line">.u_boot_list_2_cmd_3</span><br><span class="line">               0x000000008788a418        0x0 cmd/built-in.o</span><br><span class="line">.u_boot_list_2_cmd_3</span><br><span class="line">               0x000000008788a418        0x0 common/built-in.o</span><br></pre></td></tr></table></figure>

<ul>
<li><code>.u_boot_list_2_driver</code></li>
</ul>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">.u_boot_list_2_driver_1</span><br><span class="line">               0x000000008788a418        0x0 drivers/built-in.o</span><br><span class="line">.u_boot_list_2_driver_1</span><br><span class="line">               0x000000008788a418        0x0 lib/built-in.o</span><br><span class="line">.u_boot_list_2_driver_2_74x164</span><br><span class="line">               0x000000008788a418       0x48 drivers/gpio/built-in.o</span><br><span class="line">               0x000000008788a418                _u_boot_list_2_driver_2_74x164</span><br><span class="line">/*中间省略*/</span><br><span class="line">.u_boot_list_2_driver_2_usb_storage_blk</span><br><span class="line">               0x000000008788abb0       0x48 common/built-in.o</span><br><span class="line">               0x000000008788abb0                _u_boot_list_2_driver_2_usb_storage_blk</span><br><span class="line">.u_boot_list_2_driver_3</span><br><span class="line">               0x000000008788abf8        0x0 drivers/built-in.o</span><br><span class="line">.u_boot_list_2_driver_3</span><br><span class="line">               0x000000008788abf8        0x0 lib/built-in.o</span><br></pre></td></tr></table></figure>

<p>可以看到这里两种数组都是<code>&quot;.u_boot_list_2_&quot;#_list&quot;_1&quot;</code>开始，<code>&quot;.u_boot_list_2_&quot;#_list&quot;_3&quot;</code>结束，中间的<code>&quot;.u_boot_list_2_&quot;#_list&quot;_2&quot;</code>是各个命令，我们可以看一下链接文件u-boot.lds（<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/01_uboot/01_gpio_cmd/u-boot.lds">01_uboot&#x2F;01_gpio_cmd&#x2F;u-boot.lds · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国 (gitee.com)</a>）里面有这么一段：</p>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">. = .;</span><br><span class="line">. = ALIGN(4);</span><br><span class="line">.u_boot_list : &#123;</span><br><span class="line"> KEEP(*(SORT(.u_boot_list*)));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>从链接脚本中，我们可以知道<code>.u_boot_list</code>开头的段都会按照字符顺序进行排序，如<code>.u_boot_list_3*</code>、<code>.u_boot_list_2*</code> 、<code>.u_boot_list_1*</code> ，则链接器链接的时候就以<code>.u_boot_list_1*</code> 、<code>.u_boot_list_2*</code> 、<code>.u_boot_list_3*</code> 的顺序进行链接。</p>
<p>为了方便获取起始和结束地址，在所有<code>.u_boot_list_2*</code>段前面插入一个<code>.u_boot_list_1</code>段，在<code>u_boot_list_2*</code>段后面插入一个<code>.u_boot_list_3</code>段，这两个段不分配任何内存，让<code>u_boot_list_1</code>指向<code>.u_boot_list_2*</code>开始的起始地址，让<code>u_boot_list_3</code>指向最后<code>.u_boot_list_2*</code>最后一个地址的下一个地址，这样{（<code>.u_boot_list_3</code>）-（<code>.u_boot_list_1</code>）&#x3D;（<code>.u_boot_list_2*</code>所占内存大小）}，在这里可以使用一个空数组start[0]进行定位，因为数组长度为0，所以不分配内存，但是它指向的地址时当前所在位置，他赋予属性，将他放在<code>.u_boot_list_1</code>段，这样start[0]就可以指向下一个段的起始地址，下个段为<code>.u_boot_list_2*</code>。</p>
<p>同理在<code>.u_boot_list_2*</code>末尾插入<code>.u_boot_list_3</code>段，使用一个空数组end[0]，并且将它放到<code>.u_boot_list_3</code>段，这样end[0]就指向<code>.u_boot_list_3</code>后面的第一个地址。如果start和end强制为char型指针，那（end-start）就是<code>.u_boot_list_2*</code>所占内存大小，如果是_type类型，那么（<code>.u_boot_list_2*</code>所占内存大小）&#x3D;sizeof(_type)*(start-end)</p>
<p>同理也可以计算<code>_u_boot_list_2_##_list##_2_##_name</code>的内存大小或数组元素个数，为什么将这个_list列表称之为数组，是因为在ll_entry_declare宏定义时就要求相同的_list下的元素的数据类型必须一致，例如_list名为cmd的数据类型就为struct cmd_tbl_t结构体，driver的数据类型为struct driver结构体。因为链接脚本中对<code>.u_boot_list*</code>段进行了排序，<code>.u_boot_list_2_cmd_2_*</code>段会排放在一起，自然而然这些段中的变量<code>_u_boot_list_2_cmd_2_*</code>就会被放在连续的地址空间，数据类型又是相同的，所以和数组的属性一致，因此将之称之为数组。</p>
<p>同上在<code>.u_boot_list_2_cmd_2_*</code>段的起始地址插一个<code>.u_boot_list_2_cmd_1</code>段，并且存放一个空数组start[0]，让指向<code>_u_boot_list_2_cmd_2_*</code> 数组第一个变量，在<code>_u_boot_list_2_cmd_2_*</code>数组结束后插入一个<code>.u_boot_list_2_cmd_3</code>段，存放一个空数组end[0]，end指向<code>_u_boot_list_2_cmd_2_*</code>数组列结束后的第一个地址，然后将end和start强制转化为struct cmd_tbl_t结构体指针，这样（end-start）&#x3D;（<code>_u_boot_list_2_cmd_2_*</code>）数组元素的个数。</p>
<h5 id="5-1-1-6-u-boot-list-2-cmd-1怎么来的？"><a href="#5-1-1-6-u-boot-list-2-cmd-1怎么来的？" class="headerlink" title="5.1.1.6 u_boot_list_2_cmd_1怎么来的？"></a><font size=3>5.1.1.6 u_boot_list_2_cmd_1怎么来的？</font></h5><p>上面其实分析完有一个疑问，就是<code>.u_boot_list_2_cmd_1</code>和<code>.u_boot_list_2_cmd_3</code>是怎么来的？我们每次通过<code>U_BOOT_CMD</code>定义命令的时候会使用ll_entry_declare宏来定义一个链接到<code>.u_boot_list_2_cmd_2_*</code>段的<code>_u_boot_list_2_cmd_2_*</code>命令，但是用于寻找开始起始和末尾的两个段怎么被插入的？我其实找了半天，最后就发现，这两个段相关的关键词就在这两个宏里面：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_end(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char end[0] __aligned(4) __attribute__((unused,		\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_3&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;end;							\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p>别的地方找不到，那么肯定是在某个地方使用了这两个宏的，所以在链接的时候才会出现这两个段。虽然找不到，但是我们可以加打印啊，虽然这样其实不是很合理，但是试一下吧，看一看程序一开始从哪出现的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	printf(<span class="string">&quot;[%s:%d] _list=%s, %s\n&quot;</span>, __FUNCTION__, __LINE__, #_list, <span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>);\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_end(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	printf(<span class="string">&quot;[%s:%d] _list=%s, %s\n&quot;</span>, __FUNCTION__, __LINE__, #_list, <span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_3&quot;</span>);\</span></span><br><span class="line"><span class="meta">	static char end[0] __aligned(4) __attribute__((unused,		\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_3&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;end;							\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p>然后打印信息如下：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117093804710.png" alt="image-20241117093804710" />

<p>发现其实就是在这个寻找命令的地方，所以其实可能并没有什么用，据我推测，可能是因为宏里面是static类型的数组名，虽然是0个元素，不分配内存，但是还是会被链接到对应的段中。我们可以试一下，就随便定义一个吧，在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/linker_lists.h">linker_lists.h - include&#x2F;linker_lists.h</a>里面定义：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_start_demo(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char start[0] __aligned(4) __attribute__((unused,	\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_demo_1&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;start;						\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_end_demo(_type, _list)					\</span></span><br><span class="line"><span class="meta">(&#123;									\</span></span><br><span class="line"><span class="meta">	static char end[0] __aligned(4) __attribute__((unused,		\</span></span><br><span class="line"><span class="meta">		section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_demo_3&quot;</span>)));			\</span></span><br><span class="line"><span class="meta">	(_type *)&amp;end;							\</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br></pre></td></tr></table></figure>

<p>就这样：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117095724996.png" alt="image-20241117095724996" />

<p>然后我们去随便定义一个变量，让这个宏展开，就去<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/main.c#L41">main.c - common&#x2F;main.c</a>里面搞：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> *p1 = ll_entry_start_demo(<span class="type">int</span>, sumu);</span><br><span class="line"><span class="type">int</span> *p2 = ll_entry_end_demo(<span class="type">int</span>, sumu);</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117095945588.png" alt="image-20241117095945588" />

<p>然后我们编译一下，编译完去看映射文件，搜索一下sumu关键词，发现啥都没有，大概应该是这两个局部变量没有使用，直接被优化掉了，根本没有参与链接，那么我们使用一下这两个变量：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> *p1 = ll_entry_start_demo(<span class="type">int</span>, sumu);</span><br><span class="line"><span class="type">int</span> *p2 = ll_entry_end_demo(<span class="type">int</span>, sumu);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p1=%p p2=%p\n&quot;</span>, p1, p2);</span><br></pre></td></tr></table></figure>

<p>然后再编译，再搜索，就会发现，这两个段出现了：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241117100435266.png" alt="image-20241117100435266" />

<p>其实通过以上实验就可以知道，这两个段，只要有调用的地方，就一定会插入进去。</p>
<h4 id="5-1-2-find-cmd-tbl"><a href="#5-1-2-find-cmd-tbl" class="headerlink" title="5.1.2 find_cmd_tbl()"></a><font size=3>5.1.2 find_cmd_tbl()</font></h4><p>最后我们再看一下这个find_cmd_tbl()函数，它定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/command.c#L85">command.c - common&#x2F;command.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* find command table entry for a command */</span></span><br><span class="line"><span class="type">cmd_tbl_t</span> *<span class="title function_">find_cmd_tbl</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cmd, <span class="type">cmd_tbl_t</span> *table, <span class="type">int</span> table_len)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_CMDLINE</span></span><br><span class="line">	<span class="type">cmd_tbl_t</span> *cmdtp;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *cmdtp_temp = table;	<span class="comment">/* Init value */</span></span><br><span class="line">	<span class="type">const</span> <span class="type">char</span> *p;</span><br><span class="line">	<span class="type">int</span> len;</span><br><span class="line">	<span class="type">int</span> n_found = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!cmd)</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Some commands allow length modifiers (like &quot;cp.b&quot;);</span></span><br><span class="line"><span class="comment">	 * compare command name only until first dot.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	len = ((p = <span class="built_in">strchr</span>(cmd, <span class="string">&#x27;.&#x27;</span>)) == <span class="literal">NULL</span>) ? <span class="built_in">strlen</span> (cmd) : (p - cmd);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (cmdtp = table; cmdtp != table + table_len; cmdtp++) &#123;</span><br><span class="line">		<span class="keyword">if</span> (<span class="built_in">strncmp</span>(cmd, cmdtp-&gt;name, len) == <span class="number">0</span>) &#123;</span><br><span class="line">			<span class="keyword">if</span> (len == <span class="built_in">strlen</span>(cmdtp-&gt;name))</span><br><span class="line">				<span class="keyword">return</span> cmdtp;	<span class="comment">/* full match */</span></span><br><span class="line"></span><br><span class="line">			cmdtp_temp = cmdtp;	<span class="comment">/* abbreviated command ? */</span></span><br><span class="line">			n_found++;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (n_found == <span class="number">1</span>) &#123;			<span class="comment">/* exactly one match */</span></span><br><span class="line">		<span class="keyword">return</span> cmdtp_temp;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/* CONFIG_CMDLINE */</span></span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="literal">NULL</span>;	<span class="comment">/* not found or ambiguous command */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个函数主要是在命令列表里面找到我们的命令。我们可以看一下传入的参数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">find_cmd_tbl(cmd, start, len);</span><br></pre></td></tr></table></figure>

<p>cmd就是前面我们的gpio命令字符串，start就是<code>.u_boot_list_2_cmd_1</code>的地址，len就是<code>_u_boot_list_2_cmd_2_*</code>列表的长度。所以这里其实就是把<code>_u_boot_list_2_cmd_2_*</code>列表的数据遍历一遍，找到name为字符串的命令，找到了就返回这个命令的地址，没找到就返回一个NULL。返回的这个命令包含哪些信息？我们来看一下这个<code>cmd_tbl_t</code>类型就知道了<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/include/command.h#L29">command.h - include&#x2F;command.h</a>，其实前面分析gpio命令的时候有了解过的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">cmd_tbl_s</span> &#123;</span></span><br><span class="line">	<span class="type">char</span> *name;		<span class="comment">/* Command Name			*/</span></span><br><span class="line">	<span class="type">int</span>  maxargs;	<span class="comment">/* maximum number of arguments	*/</span></span><br><span class="line">	<span class="type">int</span>  (*cmd_rep)(<span class="keyword">struct</span> cmd_tbl_s *cmd, <span class="type">int</span> flags, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[], <span class="type">int</span> *repeatable); <span class="comment">/* Implementation function	*/</span></span><br><span class="line">	<span class="type">int</span>  (*cmd)(<span class="keyword">struct</span> cmd_tbl_s *, <span class="type">int</span>, <span class="type">int</span>, <span class="type">char</span> * <span class="type">const</span> []);</span><br><span class="line">	<span class="type">char</span> *usage; <span class="comment">/* Usage message	(short)	*/</span></span><br><span class="line">	<span class="type">char</span> *help;  <span class="comment">/* Help  message	(long)	*/</span></span><br><span class="line">	<span class="type">int</span>  (*complete)(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[], <span class="type">char</span> last_char, <span class="type">int</span> maxv, <span class="type">char</span> *cmdv[]);<span class="comment">/* do auto completion on the arguments */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>结合展开的gpio命令变量：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> _u_boot_list_2_cmd_2_gpio __aligned(<span class="number">4</span>)</span><br><span class="line">			__attribute__((unused, section(<span class="string">&quot;.u_boot_list_2_cmd_2_gpio&quot;</span>))) = &#123;</span><br><span class="line">			<span class="string">&quot;gpio&quot;</span>,</span><br><span class="line">            <span class="number">4</span>,</span><br><span class="line">            cmd_never_repeatable,</span><br><span class="line">            do_gpio,</span><br><span class="line">			<span class="string">&quot;query and control gpio pins&quot;</span>,</span><br><span class="line">			<span class="string">&quot;&lt;input|set|clear|toggle&gt; &lt;pin&gt;\n&quot;</span></span><br><span class="line">            <span class="string">&quot;    - input/set/clear/toggle the specified pin\n&quot;</span></span><br><span class="line">	        <span class="string">&quot;gpio status [-a] [&lt;bank&gt; | &lt;pin&gt;]  - show [all/claimed] GPIOs&quot;</span>,</span><br><span class="line">			<span class="literal">NULL</span>,&#125;;</span><br></pre></td></tr></table></figure>

<p>可以知道这个find_cmd_tbl()函数返回的就这些数据：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">_u_boot_list_2_cmd_2_gpio.name = <span class="string">&quot;gpio&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_gpio.maxargs = <span class="number">4</span>;</span><br><span class="line">_u_boot_list_2_cmd_2_gpio.cmd_rep = cmd_never_repeatable;</span><br><span class="line">_u_boot_list_2_cmd_2_gpio.cmd = do_gpio;</span><br><span class="line">_u_boot_list_2_cmd_2_gpio.usage = <span class="string">&quot;query and control gpio pins&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_gpio.help = <span class="string">&quot;&lt;input|set|clear|toggle&gt; &lt;pin&gt;\n&quot;</span></span><br><span class="line">            					 <span class="string">&quot;    - input/set/clear/toggle the specified pin\n&quot;</span></span><br><span class="line">	       						 <span class="string">&quot;gpio status [-a] [&lt;bank&gt; | &lt;pin&gt;]  - show [all/claimed] GPIOs&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_gpio.complete = <span class="literal">NULL</span>;</span><br></pre></td></tr></table></figure>

<h3 id="5-2-cmd-call"><a href="#5-2-cmd-call" class="headerlink" title="5.2 cmd_call()"></a><font size=3>5.2 cmd_call()</font></h3><p>上面我们已经找到了gpio命令的信息，接下来就该执行了，我们看cmd_process函数中是调用了cmd_call()函数来执行，这个函数定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/command.c#L560">command.c - common&#x2F;command.c</a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call a command function. This should be the only route in U-Boot to call</span></span><br><span class="line"><span class="comment"> * a command, so that we can track whether we are waiting for input or</span></span><br><span class="line"><span class="comment"> * executing a command.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @param cmdtp		Pointer to the command to execute</span></span><br><span class="line"><span class="comment"> * @param flag		Some flags normally 0 (see CMD_FLAG_.. above)</span></span><br><span class="line"><span class="comment"> * @param argc		Number of arguments (arg 0 must be the command text)</span></span><br><span class="line"><span class="comment"> * @param argv		Arguments</span></span><br><span class="line"><span class="comment"> * @param repeatable	Can the command be repeated</span></span><br><span class="line"><span class="comment"> * @return 0 if command succeeded, else non-zero (CMD_RET_...)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">cmd_call</span><span class="params">(<span class="type">cmd_tbl_t</span> *cmdtp, <span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params">		    <span class="type">int</span> *repeatable)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> result;</span><br><span class="line"></span><br><span class="line">	result = cmdtp-&gt;cmd_rep(cmdtp, flag, argc, argv, repeatable);</span><br><span class="line">	<span class="keyword">if</span> (result)</span><br><span class="line">		debug(<span class="string">&quot;Command failed, result=%d\n&quot;</span>, result);</span><br><span class="line">	<span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到这个是在调用cmd_rep这个函数指针，前面在gpio命令中，这个函数指针指向了cmd_never_repeatable()。</p>
<h3 id="5-3-cmd-never-repeatable"><a href="#5-3-cmd-never-repeatable" class="headerlink" title="5.3 cmd_never_repeatable()"></a><font size=3>5.3 cmd_never_repeatable()</font></h3><p>我们再去详细看一下这个cmd_never_repeatable()函数，它是定义在<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/u-boot/v2019.04/source/common/command.c#L532">command.c - common&#x2F;command.c </a>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">cmd_never_repeatable</span><span class="params">(<span class="type">cmd_tbl_t</span> *cmdtp, <span class="type">int</span> flag, <span class="type">int</span> argc,</span></span><br><span class="line"><span class="params">			 <span class="type">char</span> * <span class="type">const</span> argv[], <span class="type">int</span> *repeatable)</span></span><br><span class="line">&#123;</span><br><span class="line">	*repeatable = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> cmdtp-&gt;cmd(cmdtp, flag, argc, argv);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里又调用了cmd函数指针，前面参数一路传进来，其实这里的cmd在gpio命令中就是do_gpio()函数。</p>
<h2 id="6-总结一下"><a href="#6-总结一下" class="headerlink" title="6. 总结一下"></a><font size=3>6. 总结一下</font></h2><p>前面经过一步一步的分析，最终执行到go_gpio()函数，我们来回顾一下这个过程：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/LV05-02-U-Boot-04-uboot%E4%B8%8B%E5%91%BD%E4%BB%A4%E7%9A%84%E6%89%A7%E8%A1%8C/img/image-20241116190843990.png" alt="image-20241116190843990" />

<p>函数调用关系如上图所示。</p>
<blockquote>
<p>参考资料：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_39752170/article/details/114435809">u-boot的linker list源码分析_uboot env callback-CSDN博客</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/jianhua1992/p/16852784.html">链接脚本(Linker Scripts)语法和规则解析(自官方手册) - BSP-路人甲 - 博客园 (cnblogs.com)</a></p>
</blockquote>

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/14f3979e.html">LV05-02-U-Boot-04-uboot下命令的执行</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2024年11月19日 - 23:38</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/14f3979e.html" title="LV05-02-U-Boot-04-uboot下命令的执行">https://sumumm.github.io/post/14f3979e.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/LV05-%E7%B3%BB%E7%BB%9F%E9%95%9C%E5%83%8F/" rel="tag"><i class="fa fa-tag"></i> LV05-系统镜像</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/67dde56a.html" rel="prev" title="LV05-02-U-Boot-05-uboot下自定义一个命令">
                  <i class="fa fa-angle-left"></i> LV05-02-U-Boot-05-uboot下自定义一个命令
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/1ee5b7e1.html" rel="next" title="LV05-02-U-Boot-03-uboot下GPIO的控制">
                  LV05-02-U-Boot-03-uboot下GPIO的控制 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
